Previous Book Contents Book Index Next

Inside Macintosh: 3D Graphics Programming With QuickDraw 3D /
Chapter 4 - Geometric Objects


Using Geometric Objects

QuickDraw 3D provides routines that you can use to create and edit geometric objects, get and set attributes for those objects, and perform other geometric operations. This section illustrates how to create and delete some geometric objects and how to traverse the parts of a mesh.

Creating and Deleting Geometric Objects

As you saw briefly in the chapter "Introduction to QuickDraw 3D," QuickDraw 3D supports both immediate and retained modes of defining and rendering a model. Which mode you employ in any particular instance depends on the needs of your application. As suggested earlier, if much of the model remains unchanged from frame to frame, you should use retained mode imaging to create and draw the model. If, however, many parts of the model do change from frame to frame, you should use immediate mode imaging, creating and rendering a model on a shape-by-shape basis.

Listing 4-1 illustrates how to create a retained box.

Listing 4-1 Creating a retained box

TQ3GeometryObject       myBox;
TQ3BoxData              myBoxData;

Q3Point3D_Set(&myBoxData.origin, 1.0, 1.0, 1.0);
Q3Vector3D_Set(&myBoxData.orientation, 0, 2.0, 0);
Q3Vector3D_Set(&myBoxData.minorAxis, 2.0, 0, 0);
Q3Vector3D_Set(&myBoxData.majorAxis, 0, 0, 2.0);
myBox = Q3Box_New(&myBoxData);
Once the code in Listing 4-1 has been executed, the variable myBox contains a reference to the new box. You can then reuse or dispose of the myBoxData structure, because all subsequent operations on the retained box are performed using myBox. For example, to submit the box for drawing, picking, bounding, or writing, you can execute the following line of code inside a rendering, picking, bounding, or writing loop:

myStatus = Q3Object_Submit(myBox, myView);
To dispose of the retained box, you can call the Q3Object_Dispose function,
as follows:

myStatus = Q3Object_Dispose(myBox);
Listing 4-2 illustrates how to create an immediate box.

Listing 4-2 Creating an immediate box

TQ3BoxData              myBoxData;

Q3Point3D_Set(&myBoxData.origin, 1.0, 1.0, 1.0);
Q3Vector3D_Set(&myBoxData.orientation, 0, 2.0, 0);
Q3Vector3D_Set(&myBoxData.minorAxis, 2.0, 0, 0);
Q3Vector3D_Set(&myBoxData.majorAxis, 0, 0, 2.0);
As you can see, you do not have to call any QuickDraw 3D routine to create an immediate box; instead, you simply define the box data in a structure of type TQ3BoxData. To draw an immediate box, you call the Q3Box_Submit function (inside a rendering loop), as follows:

myStatus = Q3Box_Submit(myBox, myView);
Because you didn't create any retained entity, you do not need to dispose of the immediate box.

Creating a Mesh

As you saw earlier (in "Meshes," beginning on page 4-6), you create a mesh
by calling Q3Mesh_New to create a new empty mesh and then by calling Q3Mesh_VertexNew and Q3Mesh_FaceNew to explicitly add vertices and faces
to the mesh. Listing 4-3 illustrates how to create a simple mesh using these functions. It also shows how to attach a custom surface parameterization to
a mesh face, so that a texture can be mapped onto the face.

Listing 4-3 Creating a simple mesh

TQ3GroupObject MyBuildMesh (void)
{
   TQ3ColorRGB                myMeshColor;
   TQ3GroupObject             myModel;
   static TQ3Vertex3D         vertices[9] = {
      { { -0.5,  0.5, 0.0 }, NULL },
      { { -0.5, -0.5, 0.0 }, NULL },
      { {  0.0, -0.5, 0.3 }, NULL },
      { {  0.5, -0.5, 0.0 }, NULL },
      { {  0.5,  0.5, 0.0 }, NULL },
      { {  0.0,  0.5, 0.3 }, NULL },
      { { -0.4,  0.2, 0.0 }, NULL },
      { {  0.0,  0.0, 0.0 }, NULL },
      { { -0.4, -0.2, 0.0 }, NULL }};
   static TQ3Param2D          verticesUV[9] = {
      {0.0, 1.0}, {0.0, 0.0}, {0.5, 0.0}, {1.0, 0.0},
      {1.0, 1.0}, {0.5, 1.0}, {0.1, 0.8}, {0.5, 0.5},
      {0.1, 0.4}};
   
   TQ3MeshVertex              myMeshVertices[9];
   TQ3GeometryObject          myMesh;
   TQ3MeshFace                myMeshFace;
   TQ3AttributeSet            myFaceAttrs;
   unsigned long              i;

   myMesh = Q3Mesh_New();     /*create new empty mesh*/

   Q3Mesh_DelayUpdates(myMesh);/*turn off mesh updating*/

   /*Add vertices and surface parameterization to mesh.*/
   for (i = 0; i < 9; i++) 
   {
      TQ3AttributeSet         myVertAttrs;
      
      myMeshVertices[i] = Q3Mesh_VertexNew(myMesh, &vertices[i]);
      myVertAttrs = Q3AttributeSet_New();
      Q3AttributeSet_Add
               (myVertAttrs, kQ3AttributeTypeSurfaceUV, &verticesUV[i]);
      Q3Mesh_SetVertexAttributeSet(myMesh, myMeshVertices[i], myVertAttrs);
      Q3Object_Dispose(myVertAttrs);
   }

   myFaceAttrs = Q3AttributeSet_New();
   myMeshColor.r = 0.3;
   myMeshColor.g = 0.9;
   myMeshColor.b = 0.5;
   Q3AttributeSet_Add
               (myFaceAttrs, kQ3AttributeTypeDiffuseColor, &myMeshColor);

   myMeshFace = Q3Mesh_FaceNew(myMesh, 6, myMeshVertices, myFaceAttrs);

   Q3Mesh_FaceToContour(myMesh, myMeshFace, 
                  Q3Mesh_FaceNew(myMesh, 3, &myMeshVertices[6], NULL));

   Q3Mesh_ResumeUpdates(myMesh);

   myModel = Q3OrderedDisplayGroup_New();
   Q3Group_AddObject(myModel, myMesh);
   Q3Object_Dispose(myFaceAttrs);
   Q3Object_Dispose(myMesh);
   return (myModel);
}
The new mesh created by MyBuildMesh is a retained object. Note that you need to call Q3Mesh_New before you call Q3Mesh_VertexNew and Q3Mesh_FaceNew. Also, the call to Q3Mesh_FaceToContour destroys any attributes associated with the mesh face that is turned into a contour.

Traversing a Mesh

QuickDraw 3D supplies a large number of functions that you can use to traverse a mesh by iterating through the various distinguishable parts of the mesh (that is, through the faces, vertices, edges, contours, or components in the mesh). For example, you can operate on each face of a mesh by calling the Q3Mesh_FirstMeshFace function to get the first face in the mesh and then by calling Q3Mesh_NextMeshFace to get each successive face in the mesh. When you call Q3Mesh_FirstMeshFace, you specify a mesh and a mesh iterator structure, which QuickDraw 3D fills in with information about its current position while traversing a mesh. You must pass that same mesh iterator structure to Q3Mesh_NextMeshFace when you get successive faces in the mesh. Listing 4-4 illustrates how to use these routines to operate on all faces in a mesh.

Listing 4-4 Iterating through all faces in a mesh

TQ3Status MySetMeshFacesDiffuseColor (TQ3GeometryObject myMesh, 
                                                TQ3ColorRGB color) 
{
   TQ3MeshFace          myFace;
   TQ3MeshIterator      myIter;
   TQ3Status            myErr;
   TQ3AttributeSet      mySet;

   for (myFace = Q3Mesh_FirstMeshFace(myMesh, &myIter); 
        myFace;
        myFace = Q3Mesh_NextMeshFace(&myIter)) {

      /*Get the current attribute set of the current face.*/
      myErr = Q3Mesh_GetFaceAttributeSet(myMesh, myFace, &mySet);
      if (myErr == kQ3Failure) return (kQ3Failure);
      
      /*Add the color attribute to the face attribute set.*/
      myErr = Q3AttributeSet_Add((TQ3AttributeSet)mySet, 
                        kQ3AttributeTypeDiffuseColor, &color);
      if (myErr == kQ3Failure) return (kQ3Failure);

      /*Set the attribute set of the current face.*/
      myErr = Q3Mesh_SetFaceAttributeSet(myMesh, myFace, mySet);
      if (myErr == kQ3Failure) return (kQ3Failure);
   }
   return (kQ3Success);
}
QuickDraw 3D also supplies a number of C language macros that you can use to simplify your source code. For example, you can use the Q3ForEachMeshFace macro, defined like this:

#define Q3ForEachMeshFace(m,f,i)                         \
   for ( (f) = Q3Mesh_FirstMeshFace((m),(i));            \
      (f);                                               \
      (f) = Q3Mesh_NextMeshFace((i)) )
Listing 4-5 shows how to use two of these macros to attach a corner to each vertex or each face of a mesh.

Listing 4-5 Attaching corners to all vertices in all faces of a mesh

TQ3Status MyAddCornersToMesh (TQ3GeometryObject myMesh, 
                                             TQ3AttributeSet mySet)
{
   TQ3MeshFace          myFace;
   TQ3MeshVertex        myVertex;
   TQ3MeshIterator      myIter1;
   TQ3MeshIterator      myIter2;
   TQ3Status            myErr;


   Q3ForEachMeshFace(myMesh, myFace, &myIter1) {
      Q3ForEachFaceVertex(myFace, myVertex, &myIter2) {
         myErr = Q3Mesh_SetCornerAttributeSet
                  (myMesh, myFace, myVertex, mySet);
         if (myErr == kQ3Failure) return (kQ3Failure);
      }
   }
   return (kQ3Success);
}
>

Previous Book Contents Book Index Next

© Apple Computer, Inc.
11 JUL 1996